
#include "Backbone.h"
#include "Visual.h"
#include "Reason.h"
#include "Sense.h"
#include "Renderer.h"

#include <iostream>
#include <ctime>
namespace Engine {

Backbone::Context::Context() {
}

Backbone::Context::~Context() {
}

// Initializes the backbone and its main subsystems

bool Backbone::raise(int viewWidth, int viewHeight) throw(char *)
{
  	// initialize SDL.
	if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_CDROM)) {
		throw SDL_GetError();
	}
	// initialize the rendering subsystem.
	try {
		Renderer::raise(Renderer::RENDERER_SOFTWARE, viewWidth, viewHeight);
	} catch (char * err) {
		std::cerr << "Error initializing renderer: " << err << std::endl;
		return false;
	}
	// initialize the audio subsystem.
	//	- when we have one, of course.
	
	status = BACKBONE_SLEEPING;
	// Initialize the first context
	actualContext = &(context[""]);
	nextContext = NULL;
	//SDL_ShowCursor(0);
	srand(std::time(NULL));
	return true; // congratulation crew. Airborne.
}

// Awakens the backbone so it starts to pulse and do its things. Only returns when finished

void Backbone::awake()
{
	status = BACKBONE_AWAKEN;
  	Engine::Sense::keyState = SDL_GetKeyState(0);
	SDL_Event eventFrame;
	int frameBegin, frameEnd, frameDelta;
	frameBegin = frameEnd = SDL_GetTicks();
	while(1) {
		// count frame delta
		frameDelta = frameEnd - frameBegin;
		frameBegin = SDL_GetTicks();
		/* first of all, perform context switch */
		if (nextContext != NULL) {
			actualContext = nextContext;
			nextContext = NULL;
		}
		
		// deliver events
		while(SDL_PollEvent(&eventFrame)) Backbone::update(&eventFrame);
		
		// updates entities
		for (std::list<Reason *>::iterator i = actualContext->reasons.begin();
		i != actualContext->reasons.end(); i++) {
			(*i)->pulse(frameDelta);
		}
		// simulates registered physics and colisions
		// ... next contest, maybe? :)
	
		// deliver the render signal
		const Engine::Resource::Graphic * gfx;
		Resource::GraphicList allGfx;
		for (std::list<Visual *>::iterator i = actualContext->visuals.begin();
		i != actualContext->visuals.end(); i++) {
			Resource::GraphicList gfxList = (*i)->display();
			allGfx.splice(allGfx.begin(), gfxList);
		}
		allGfx.sort(Misc::lessThan<Resource::Graphic>);
		while (!allGfx.empty()) {
			gfx = allGfx.front();
			Renderer::render(*gfx);
			allGfx.pop_front();
		}
		
		Engine::Renderer::flip();
		SDL_Delay(3);
		frameEnd = SDL_GetTicks();
	} // rebegin!
}

//Add a new sense to a given context or the actual context
void Backbone::addSense(Sense * sense, std::string contextName)
{
	context[contextName].senses.push_back(sense);
}

void Backbone::dropSense(Sense * sense, std::string contextName)
{
  context[contextName].senses.remove(sense);
}

void Backbone::addReason(Reason * reason, std::string contextName)
{
  context[contextName].reasons.push_back(reason);
}

void Backbone::dropReason(Reason * reason, std::string contextName)
{
  context[contextName].reasons.remove(reason);
}

void Backbone::addVisual(Visual * visual, std::string contextName)
{
  context[contextName].visuals.push_back(visual);
}

void Backbone::dropVisual(Visual * visual, std::string contextName)
{
  context[contextName].visuals.remove(visual);
}

// Loads the selected context.
void Backbone::changeContext(std::string contextName)
{
  nextContext = &(context[contextName]);
}

void Backbone::update(SDL_Event * event)
{
	if (actualContext == NULL) return;
	/* event-phase */
	// Largely based (copied, why not?) in Tim's code in his tutorial
	// about events. Thanks, you had a lot of work coding all this.
	// By the way, I'm not happy with this implementation. At all.
	// Doing the inverse, a loop for every case inside the switch
	// would be worse, much code replication. I'm open for suggestion
	for (std::list<Sense *>::iterator i = actualContext->senses.begin();
			i != actualContext->senses.end(); i++) switch(event->type) {
			case SDL_ACTIVEEVENT: {
				switch(event->active.state) {
					case SDL_APPMOUSEFOCUS: {
						if ( event->active.gain ) (*i)->mouseFocus();
						else (*i)->mouseBlur();
						break;
					}
					case SDL_APPINPUTFOCUS: {
						if ( event->active.gain ) (*i)->inputFocus();
						else (*i)->inputBlur();
						break;
					}
					case SDL_APPACTIVE:	{
						if ( event->active.gain ) (*i)->restore();
						else (*i)->minimize();
						break;
					}
				}
				break;
			}

			case SDL_KEYDOWN: {
				(*i)->keyDown(event->key.keysym.sym,event->key.keysym.mod,event->key.keysym.unicode);
				break;
			}

			case SDL_KEYUP: {
				(*i)->keyUp(event->key.keysym.sym,event->key.keysym.mod,event->key.keysym.unicode);
				break;
			}

			case SDL_MOUSEMOTION: {
				(*i)->mouseMove(event->motion.x,event->motion.y,event->motion.xrel,event->motion.yrel,(event->motion.state&SDL_BUTTON(SDL_BUTTON_LEFT))!=0,(event->motion.state&SDL_BUTTON(SDL_BUTTON_RIGHT))!=0,(event->motion.state&SDL_BUTTON(SDL_BUTTON_MIDDLE))!=0);
				break;
			}

			case SDL_MOUSEBUTTONDOWN: {
				switch(event->button.button) {
					case SDL_BUTTON_LEFT: {
						(*i)->lButtonDown(event->button.x,event->button.y);
						break;
					}
					case SDL_BUTTON_RIGHT: {
						(*i)->rButtonDown(event->button.x,event->button.y);
						break;
					}
					case SDL_BUTTON_MIDDLE: {
						(*i)->mButtonDown(event->button.x,event->button.y);
						break;
					}
				}
				break;
			}

			case SDL_MOUSEBUTTONUP:	{
				switch(event->button.button) {
					case SDL_BUTTON_LEFT: {
						(*i)->lButtonUp(event->button.x,event->button.y);
						break;
					}
					case SDL_BUTTON_RIGHT: {
						(*i)->rButtonUp(event->button.x,event->button.y);
						break;
					}
					case SDL_BUTTON_MIDDLE: {
						(*i)->mButtonUp(event->button.x,event->button.y);
						break;
					}
				}
				break;
			}

			case SDL_JOYAXISMOTION: {
				(*i)->joyAxis(event->jaxis.which,event->jaxis.axis,event->jaxis.value);
				break;
			}

			case SDL_JOYBALLMOTION: {
				(*i)->joyBall(event->jball.which,event->jball.ball,event->jball.xrel,event->jball.yrel);
				break;
			}

			case SDL_JOYHATMOTION: {
				(*i)->joyHat(event->jhat.which,event->jhat.hat,event->jhat.value);
				break;
			}
			case SDL_JOYBUTTONDOWN: {
				(*i)->joyButtonDown(event->jbutton.which,event->jbutton.button);
				break;
			}

			case SDL_JOYBUTTONUP: {
				(*i)->joyButtonUp(event->jbutton.which,event->jbutton.button);
				break;
			}

			case SDL_QUIT: {
				//(*i)->exit();
				exit(0);
				break;
			}

			case SDL_SYSWMEVENT: {
				//Ignore
				break;
			}

			case SDL_VIDEORESIZE: {
				(*i)->resize(event->resize.w,event->resize.h);
				break;
			}

			case SDL_VIDEOEXPOSE: {
				(*i)->expose();
				break;
			}

			default: {
				(*i)->user(event->user.type,event->user.code,event->user.data1,event->user.data2);
				break;
			}
		} // end of switch (event->type) and for every entity.
		/* update phase */
}

Backbone::Backbone() {
}

Backbone::~Backbone(){
}

Backbone::BackboneStatus Backbone::status = BACKBONE_DEAD; 
std::map<std::string, Backbone::Context> Backbone::context; 
Backbone::Context * Backbone::actualContext = NULL; 
Backbone::Context * Backbone::nextContext = NULL; // Context to be loaded on the next frame

} // namespace Engine
